package com.xiyuan.smartutils.lambda;

import com.xiyuan.smartutils.Systems;
import com.xiyuan.smartutils.Validates;
import com.xiyuan.smartutils.extend.Str;
import com.xiyuan.smartutils.reflect.Reflect;

import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * the description for class LBDA
 *
 * @author xiyuan-lgz 2024-06-08 @version v1.0.0 新建与整理
 */

@SuppressWarnings("all")
class LBDA {
    
    private static Map<Object, SerializedLambda> lambdaMap = new ConcurrentHashMap<>();
    
    static SerializedLambda getSerializedLambda(Lambda lambda) {
        try {
            if (lambda == null) return null;
            
            SerializedLambda serializedLambda = lambdaMap.get(lambda);
            if (serializedLambda == null) {
                Method method = lambda.getClass().getDeclaredMethod("writeReplace");
                Reflect.setAccessible(method);
                serializedLambda = (SerializedLambda) method.invoke(lambda);
                lambdaMap.put(lambda, serializedLambda);
            }
            
            return serializedLambda;
        }
        catch (Exception e) {
            throw new RuntimeException("获取Lambda信息失败", e);
        }
    }
    
    private static Class<?> resolveClass(String name) {
        try {
            switch (name) {
                case "B":
                    return byte.class;
                case "Z":
                    return boolean.class;
                case "C":
                    return char.class;
                case "S":
                    return short.class;
                case "I":
                    return int.class;
                case "J":
                    return long.class;
                case "F":
                    return float.class;
                case "D":
                    return double.class;
                
                // 数组类
                case "[B":
                    return byte[].class;
                case "[Z":
                    return boolean[].class;
                case "[C":
                    return char[].class;
                case "[S":
                    return short[].class;
                case "[I":
                    return int[].class;
                case "[J":
                    return long[].class;
                case "[F":
                    return float[].class;
                case "[D":
                    return double[].class;
                default:
                    boolean isArr = false;
                    if (name.charAt(0) == '[') {
                        isArr = true;
                        name = name.substring(1);
                    }
                    Class<?> c = Class.forName(name, false, Thread.currentThread().getContextClassLoader());
                    return !isArr ? c : Array.newInstance(c, 0).getClass();
            }
        }
        catch (ClassNotFoundException e) {
            throw new RuntimeException("无法加载类:" + name, e);
        }
    }
    
    private static Class<?>[] resolveTypes(String methodType) {
        List<Class<?>> ls = new ArrayList<Class<?>>();
        int len = methodType.length();
        StringBuilder strb = new StringBuilder();
        boolean  iL = false, comp = false;
        for (int i = 0; i < len; i++) {
            char c = methodType.charAt(i);
            if (Validates.isWhitespace(c)) continue;
            if (c == '(') continue;
            if (c == ')' || c == ';' || comp) {
                if (strb.length() > 0) {
                    ls.add(resolveClass(strb.toString()));
                    strb.setLength(0);
                    iL = false;
                    comp = false;
                }
            }
            
            if (c == ';') continue;
            if (c == ')') break;
            
            if (c == 'L' && !iL) {
                iL = true;
                continue;
            }
            if (c == '/') c = '.';
            if (c == '[') {
            }
            
            strb.append(c);
            if (!iL && strb.length() == 1 && 'A' <= c && c <= 'Z') {
                comp = true;
            }
        }
        if (strb.length() > 0) {
            ls.add(resolveClass(strb.toString()));
        }
        
        return ls.toArray(new Class<?>[0]);
    }
    
    
    /**
     * 获取Lambda表达式返回类型
     */
    static <E> Class<E> getReturnType(SerializedLambda lambda) {
        String expr = lambda.getImplMethodSignature();
        Matcher matcher = Lambda.RETURN_TYPE_PATTERN.matcher(expr);
        if (!matcher.find()) {
            throw new RuntimeException("获取Lambda信息失败");
        }
        Class<?>[] ls = resolveTypes(matcher.group(1));
        return ls.length == 0 ? (Class<E>) void.class : (Class<E>) ls[0];
        
    }
    
    /**
     * 获取Lambda表达式的参数类型
     */
    static Class<?>[] getParameterTypes(SerializedLambda lambda) {
        return resolveTypes(lambda.getImplMethodSignature());
    }
    
    static <E> Class<E> getImplClass(SerializedLambda lambda) {
        try {
            String clsName = lambda.getImplClass().replace("/", ".");
            return (Class<E>) Class.forName(clsName, false, Thread.currentThread().getContextClassLoader());
        }
        catch (Exception e) {
            throw (e instanceof RuntimeException) ? (RuntimeException) e : new RuntimeException(e.getMessage(), e);
        }
    }
    
    static Method getImplMethod(SerializedLambda lambda) {
        Class<?> cls = getImplClass(lambda);
        String methodName = lambda.getImplMethodName();
        
        try {
            return cls.getDeclaredMethod(methodName, getParameterTypes(lambda));
        }
        catch (Exception e) {
            throw (e instanceof RuntimeException) ? (RuntimeException) e : new RuntimeException(e.getMessage(), e);
        }
    }
    
    static final Pattern PROVIDE_TYPE_PATTERN = Pattern.compile("\\((L?.*);\\)");
    
    static <E> Class<E> getInstanceProviderType(SerializedLambda serializedLambda) {
        String expr = serializedLambda.getInstantiatedMethodType();
        Matcher matcher = PROVIDE_TYPE_PATTERN.matcher(expr);
        if (!matcher.find()) {
            throw new RuntimeException("获取Lambda信息失败");
        }
        Class<?>[] ls = resolveTypes(matcher.group(1));
        if (ls.length != 1) {
            throw new RuntimeException("无法Lambda提供提供类型");
        }
        
        return (Class<E>) ls[0];
    }
}
